﻿Imports System
Imports System.CodeDom
Imports System.Collections.Generic
Imports System.Reflection
Imports System.Runtime.InteropServices
Imports System.Workflow.ComponentModel
Imports System.Workflow.ComponentModel.Compiler
Imports System.Workflow.ComponentModel.Design

Namespace System.Workflow.Activities
    Friend NotInheritable Class WebServiceCodeGenerator
        Inherits ActivityCodeGenerator
        ' Methods
        Private Function CreateOrGetServiceDeclaration(ByVal rootActivity As Activity, ByVal codeNamespaceCollection As CodeNamespaceCollection) As CodeTypeDeclaration
            Dim namespaceName As String = ""
            Dim webServiceCodeNamespace As CodeNamespace = Nothing
            Dim fullName As String = rootActivity.GetType.FullName
            Dim webserviceCodeTypeDeclaration As CodeTypeDeclaration = Nothing
            If (rootActivity.GetType.FullName.IndexOf(".") <> -1) Then
                namespaceName = rootActivity.GetType.FullName.Substring(0, rootActivity.GetType.FullName.LastIndexOf("."c))
            End If
            Dim namespace3 As CodeNamespace
            For Each namespace3 In codeNamespaceCollection
                If (namespace3.Name = namespaceName) Then
                    webServiceCodeNamespace = namespace3
                    Exit For
                End If
            Next
            If (webServiceCodeNamespace Is Nothing) Then
                webServiceCodeNamespace = Me.GetWebServiceCodeNamespace(namespaceName)
                codeNamespaceCollection.Add(webServiceCodeNamespace)
            End If
            Dim str3 As String = (fullName.Substring((fullName.LastIndexOf("."c) + 1)) & "_WebService")
            Dim declaration2 As CodeTypeDeclaration
            For Each declaration2 In webServiceCodeNamespace.Types
                If (declaration2.Name = str3) Then
                    webserviceCodeTypeDeclaration = declaration2
                    Exit For
                End If
            Next
            If (webserviceCodeTypeDeclaration Is Nothing) Then
                webserviceCodeTypeDeclaration = Me.GetWebserviceCodeTypeDeclaration(fullName.Substring((fullName.LastIndexOf("."c) + 1)))
                webServiceCodeNamespace.Types.Add(webserviceCodeTypeDeclaration)
                webServiceCodeNamespace.Imports.Add(New CodeNamespaceImport("System"))
                webServiceCodeNamespace.Imports.Add(New CodeNamespaceImport("System.Web"))
                webServiceCodeNamespace.Imports.Add(New CodeNamespaceImport("System.Web.Services"))
                webServiceCodeNamespace.Imports.Add(New CodeNamespaceImport("System.Web.Services.Protocols"))
                webServiceCodeNamespace.Imports.Add(New CodeNamespaceImport("System.Workflow.Runtime.Hosting"))
                webServiceCodeNamespace.Imports.Add(New CodeNamespaceImport("System.Workflow.Activities"))
            End If
            Return webserviceCodeTypeDeclaration
        End Function

        Public Overrides Sub GenerateCode(ByVal manager As CodeGenerationManager, ByVal obj As Object)
            Dim activity As WebServiceInputActivity = [TryCast](obj,WebServiceInputActivity)
            If (manager Is Nothing) Then
                Throw New ArgumentNullException("manager")
            End If
            If (obj Is Nothing) Then
                Throw New ArgumentNullException("obj")
            End If
            If (Not activity Is Nothing) Then
                If Not TypeOf manager.GetService(GetType(ITypeProvider)) Is ITypeProvider Then
                    Throw New InvalidOperationException(SR.GetString("General_MissingService", New Object() { GetType(ITypeProvider).FullName }))
                End If
                Dim codeNamespaceCollection As CodeNamespaceCollection = [TryCast](manager.Context.Item(GetType(CodeNamespaceCollection)),CodeNamespaceCollection)
                If (codeNamespaceCollection Is Nothing) Then
                    Throw New InvalidOperationException(SR.GetString("Error_ContextStackItemMissing", New Object() { GetType(CodeNamespaceCollection).Name }))
                End If
                Dim declaration As CodeTypeDeclaration = Me.CreateOrGetServiceDeclaration(Helpers.GetRootActivity(activity), codeNamespaceCollection)
                If (Not activity.InterfaceType Is Nothing) Then
                    Dim flag As Boolean = False
                    Dim interfaceMethod As MethodInfo = Helpers.GetInterfaceMethod(activity.InterfaceType, activity.MethodName)
                    Dim supportedLanguage As SupportedLanguages = CompilerHelpers.GetSupportedLanguage(manager)
                    Dim member As CodeTypeMember
                    For Each member In declaration.Members
                        If (TypeOf member Is CodeMemberMethod AndAlso (String.Compare(member.Name, interfaceMethod.Name, If((supportedLanguage = SupportedLanguages.CSharp), StringComparison.InvariantCulture, StringComparison.InvariantCultureIgnoreCase)) = 0)) Then
                            flag = True
                        End If
                    Next
                    If Not flag Then
                        declaration.Members.Add(Me.GetWebServiceMethodDeclaraion(interfaceMethod, activity.IsActivating, supportedLanguage))
                    End If
                End If
                MyBase.GenerateCode(manager, obj)
            End If
        End Sub

        Private Function GetWebServiceCodeNamespace(ByVal namespaceName As String) As CodeNamespace
            Return New CodeNamespace(namespaceName)
        End Function

        Private Function GetWebserviceCodeTypeDeclaration(ByVal workflowTypeName As String) As CodeTypeDeclaration
            Dim declaration As New CodeTypeDeclaration((workflowTypeName & "_WebService"))
            declaration.BaseTypes.Add(New CodeTypeReference("WorkflowWebService"))
            Dim declaration2 As New CodeAttributeDeclaration("WebServiceBinding", New CodeAttributeArgument() { New CodeAttributeArgument("ConformsTo", New CodeFieldReferenceExpression(New CodeTypeReferenceExpression("WsiProfiles"), "BasicProfile1_1")), New CodeAttributeArgument("EmitConformanceClaims", New CodePrimitiveExpression(True)) })
            declaration.CustomAttributes.Add(declaration2)
            Dim constructor As New CodeConstructor { _
                .Attributes = MemberAttributes.Public _
            }
            constructor.BaseConstructorArgs.Add(New CodeTypeOfExpression(workflowTypeName))
            declaration.Members.Add(constructor)
            Return declaration
        End Function

        Private Function GetWebServiceMethodDeclaraion(ByVal methodInfo As MethodInfo, ByVal isActivation As Boolean, ByVal language As SupportedLanguages) As CodeMemberMethod
            Dim method As New CodeMemberMethod { _
                .Attributes = MemberAttributes.Public, _
                .ReturnType = New CodeTypeReference(methodInfo.ReturnType), _
                .Name = methodInfo.Name _
            }
            Dim declaration As New CodeAttributeDeclaration("WebMethodAttribute")
            declaration.Arguments.Add(New CodeAttributeArgument("Description", New CodePrimitiveExpression(methodInfo.Name)))
            declaration.Arguments.Add(New CodeAttributeArgument("EnableSession", New CodePrimitiveExpression(False)))
            method.CustomAttributes.Add(declaration)
            Dim list As New List([Of] ParameterInfo)
            Dim expression As New CodeArrayCreateExpression { _
                .CreateType = New CodeTypeReference(GetType(Object)) _
            }
            Dim info As ParameterInfo
            For Each info In methodInfo.GetParameters
                Dim expression2 As New CodeParameterDeclarationExpression
                If (info.IsOut OrElse info.ParameterType.IsByRef) Then
                    expression2.Type = New CodeTypeReference(info.ParameterType.GetElementType.FullName)
                    expression2.Direction = If(info.IsOut, FieldDirection.Out, FieldDirection.Ref)
                    If ((expression2.Direction = FieldDirection.Out) AndAlso (language = SupportedLanguages.VB)) Then
                        expression2.CustomAttributes.Add(New CodeAttributeDeclaration(New CodeTypeReference(GetType(OutAttribute))))
                    End If
                    list.Add(info)
                Else
                    expression2.Type = New CodeTypeReference(info.ParameterType.FullName)
                End If
                expression2.Name = info.Name
                method.Parameters.Add(expression2)
                If Not info.IsOut Then
                    expression.Initializers.Add(New CodeArgumentReferenceExpression(info.Name))
                End If
            Next
            Dim expression3 As New CodeMethodInvokeExpression { _
                .Method = New CodeMethodReferenceExpression(New CodeThisReferenceExpression, "Invoke") _
            }
            expression3.Parameters.Add(New CodeTypeOfExpression(methodInfo.DeclaringType))
            expression3.Parameters.Add(New CodePrimitiveExpression(methodInfo.Name))
            expression3.Parameters.Add(New CodePrimitiveExpression(isActivation))
            expression3.Parameters.Add(expression)
            Dim num As Integer = If((methodInfo.ReturnType Is GetType(Void)), 0, 1)
            If (list.Count <> 0) Then
                Dim statement As New CodeVariableDeclarationStatement(New CodeTypeReference(New CodeTypeReference(GetType(Object)), 1), "results") { _
                    .InitExpression = expression3 _
                }
                method.Statements.Add(statement)
                Dim i As Integer
                For i = 0 To list.Count - 1
                    Dim info2 As ParameterInfo = list.Item(i)
                    Dim statement2 As New CodeAssignStatement
                    Dim expression4 As CodeExpression = New CodeArgumentReferenceExpression(info2.Name)
                    Dim expression5 As CodeExpression = New CodeCastExpression(New CodeTypeReference(info2.ParameterType.GetElementType.FullName), New CodeIndexerExpression(New CodeVariableReferenceExpression("results"), New CodeExpression() { New CodePrimitiveExpression((i + num)) }))
                    statement2.Left = expression4
                    statement2.Right = expression5
                    method.Statements.Add(statement2)
                Next i
            End If
            If (Not methodInfo.ReturnType Is GetType(Void)) Then
                Dim expression6 As CodeExpression
                If (list.Count <> 0) Then
                    expression6 = New CodeVariableReferenceExpression("results")
                Else
                    expression6 = expression3
                End If
                Dim statement3 As New CodeMethodReturnStatement(New CodeCastExpression(methodInfo.ReturnType, New CodeIndexerExpression(expression6, New CodeExpression() { New CodePrimitiveExpression(0) })))
                method.Statements.Add(statement3)
                Return method
            End If
            If ((list.Count = 0) AndAlso (methodInfo.ReturnType Is GetType(Void))) Then
                method.Statements.Add(expression3)
            End If
            Return method
        End Function

    End Class
End Namespace

